#include "qruntimedatabaseview.h"
#include "qaddruntimedatadialog.h"

#include "../../lib/corelib/stylehelper.h"
#include "../../lib/corelib/qactiontoolbar.h"
#include "../../lib/shared/qproject.h"
#include "../../include/abstractcore.h"

#include <QHeaderView>
#include <QPainter>
#include <QMessageBox>
#include <QLineEdit>
#include <QSpinBox>

#include <QValidator>
#include <QRegExp>
#include <QFile>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QComboBox>
#include <QTimeEdit>
#include <QDateEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QApplication>
#include <QStyle>
#include <QMessageBox>

QStringList      s_dataNames;
QString          s_current;

class NameValidator: public QValidator
{
public:
    NameValidator(QObject *parent=0):
        QValidator(parent),
        m_reg("[_a-zA-Z][_a-zA-Z0-9]{,64}")
    {
    }
    virtual State validate(QString &, int &) const;
protected:
    QRegExp m_reg;
};

QValidator::State NameValidator::validate(QString &input, int &) const
{
    if(!m_reg.exactMatch(input))
    {
        return Intermediate;
    }

    QStringList s=s_dataNames;
    s.removeAll(s_current);
    if(s.indexOf(input)>=0)
    {
        return Intermediate;
    }
    return Acceptable;
}

NameEdit::NameEdit(QWidget *parent):
    QLineEdit(parent)
{
    setFrame(false);
    setValidator(new NameValidator(this));
    setContextMenuPolicy(Qt::NoContextMenu);
    connect(this,SIGNAL(textChanged(QString)),this,SLOT(slotTextChanged(QString)));
}

void NameEdit::markIntermediateState()
{
    if(this->hasAcceptableInput())
    {
        setPalette(QPalette());
    }
    else
    {
        QPalette pal=this->palette();
        pal.setColor(QPalette::Active,QPalette::Text,Qt::red);
        setPalette(pal);
    }
}

void NameEdit::setValue(const QString &str)
{
    m_text=str;
    s_current=str;
    markIntermediateState();
    setText(str);
}

QString NameEdit::value() const
{
    if(hasAcceptableInput())
    {
        return text();
    }
    else
    {
        return m_text;
    }
}

void NameEdit::slotTextChanged(const QString &text)
{
    markIntermediateState();
}

QWidget* RunTableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QWidget *ret=NULL;


    switch(index.column())
    {
    case 0:
    {
        NameEdit *edit=new NameEdit(parent);
        edit->setAlignment(Qt::AlignCenter);
        ret=edit;
    }
    break;
    case 1:
    {
        QString type=index.model()->data(index.model()->index(index.row(),2,index.parent())).toString();
        if(type=="Time")
        {
            QTimeEdit *e=new QTimeEdit(parent);
            e->setFrame(false);
            e->setTime(index.data(Qt::EditRole).toTime());
            e->setAlignment(Qt::AlignCenter);
            ret=e;
        }
        else if(type=="Date")
        {
            QDateEdit *d=new QDateEdit(parent);
            d->setFrame(false);
            d->setDate(index.data(Qt::EditRole).toDate());
            d->setAlignment(Qt::AlignCenter);
            ret=d;
        }
        else if(type=="DateTime")
        {
            QDateTimeEdit *dt=new QDateTimeEdit(parent);
            dt->setFrame(false);
            dt->setDateTime(index.data(Qt::EditRole).toDateTime());
            dt->setAlignment(Qt::AlignCenter);
            ret=dt;
        }
        else if(type=="Integer")
        {
            QSpinBox *sp=new QSpinBox(parent);
            sp->setFrame(false);
            sp->setRange(0x80000000,0x7fffffff);
            sp->setValue(index.data(Qt::EditRole).toInt());
            sp->setAlignment(Qt::AlignCenter);
            ret=sp;
        }
        else if(type=="Float")
        {
            QDoubleSpinBox *dsp=new QDoubleSpinBox(parent);
            dsp->setFrame(false);
            dsp->setDecimals(6);
            dsp->setRange(-99999999.0,999999999);
            dsp->setValue(index.data(Qt::EditRole).toDouble());
            dsp->setAlignment(Qt::AlignCenter);
            ret=dsp;
        }
        else if(type=="String")
        {
            QLineEdit *e=new QLineEdit(parent);
            e->setFrame(false);
            e->setText(index.data(Qt::EditRole).toString());
            e->setAlignment(Qt::AlignCenter);
            ret=e;
        }
    }
        break;
    case 2:
    {
        QComboBox *co=new QComboBox(parent);
        co->setFrame(false);
        co->addItems(QRuntimeData::typeList());
        ret=co;
        co->setCurrentIndex(co->findText(index.data().toString()));
    }
        break;
    case 5:
    {
        QSpinBox *box=new QSpinBox(parent);
        box->setFrame(false);
        box->setMinimum(0);
        box->setMaximum(65535);
        box->setAlignment(Qt::AlignCenter);

        ret=box;
    }
        break;
    case 6:
    {
        QLineEdit *e=new QLineEdit(parent);
        e->setFrame(false);
        e->setText(index.data().toString());
        ret=e;
    }
    }
    return ret;
}

void RunTableDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
{
    if(index.column()==1)
    {
        ((NameEdit*)editor)->setText(index.data(Qt::DisplayRole).toString());
    }
    else if(index.column()==2)
    {
        QComboBox *co=(QComboBox*)editor;
        co->setCurrentIndex(co->findText(index.data(Qt::DisplayRole).toString()));
    }
    else
    {
        QItemDelegate::setEditorData(editor,index);
    }
}

void RunTableDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{

    if(index.column()==1)
    {
        QVariant v;
        QString type=index.model()->data(index.model()->index(index.row(),2,index.parent())).toString();
        if(type=="Time")
        {
            QTimeEdit *e=(QTimeEdit*)editor;
            v.setValue<QTime>(e->time());
        }
        else if(type=="Date")
        {
            QDateEdit *d=(QDateEdit*)editor;
            v.setValue<QDate>(d->date());
        }
        else if(type=="DateTime")
        {
            QDateTimeEdit *dt=(QDateTimeEdit*)editor;
            v.setValue<QDateTime>(dt->dateTime());
        }
        else if(type=="Integer")
        {
            QSpinBox *sp=(QSpinBox*)editor;
            v=sp->value();
        }
        else if(type=="Float")
        {
            QDoubleSpinBox *dsp=(QDoubleSpinBox*)editor;
            v=dsp->value();
        }
        else if(type=="String")
        {
            QLineEdit *e=(QLineEdit*)editor;
            v=e->text();
        }
        m_view->model()->setData(index,v,Qt::UserRole);
        m_view->model()->setData(index,v.toString(),Qt::EditRole);
    }
    else if(index.column()==2)
    {
        model->setData(index,((QComboBox*)editor)->currentText(),Qt::EditRole);
    }
    else
    {
        QItemDelegate::setModelData(editor,model,index);
    }
}

void RunTableDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(editor!=NULL)
    {
        editor->setGeometry(option.rect.adjusted(0,index.row()==0?1:0,-1,-1));
    }
}

QSize RunTableDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize sz=QItemDelegate::sizeHint(option,index);
    sz.setHeight(20);
    return sz;
}

void RunTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItemV3 opt=option;

    QColor c;
    QString category;
    if(index.child(0,0).isValid())
    {
        category=index.model()->data(index.model()->index(index.row(),0,index.parent())).toString();
        opt.font.setBold(true);
        opt.font.setPointSize(11);
    }
    else
    {
        category=index.model()->data(index.model()->index(index.parent().row(),0,index.parent().parent())).toString();
    }
    c=m_view->getColor(category);
    if(opt.features & QStyleOptionViewItemV2::Alternate)
    {
        c=c.lighter(112);
    }
    painter->fillRect(opt.rect,c);

    opt.state &=~ QStyle::State_HasFocus;
    QItemDelegate::paint(painter,opt,index);
    opt.palette.setCurrentColorGroup(QPalette::Active);
    QColor color=static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor,&opt));
    painter->setPen(color);
    painter->drawLine(opt.rect.bottomLeft(),opt.rect.bottomRight());

    if(index.column()<index.model()->columnCount(index)-1 && !index.child(0,0).isValid())
    {
        int right=opt.direction==Qt::LeftToRight?opt.rect.right():opt.rect.left();
        painter->drawLine(right,opt.rect.y(),right,opt.rect.bottom());
    }
}



QRuntimeDatabaseView::QRuntimeDatabaseView(AbstractCore *core, QWidget *parent):
    QTreeWidget(parent),
    m_core(core)
{
    this->setFrameStyle(QFrame::NoFrame);
    this->header()->setMovable(false);
    this->setAlternatingRowColors(true);

    this->setHeaderLabels(QStringList()<<"Name"<<"Value"<<"Type"<<"Hode On"<<"Save"<<"Save Time"<<"Information");
    this->setItemDelegate(new RunTableDelegate(this));


    m_toolbar=new QActionToolBar(this);

    QList<QAction*> list;

    QAction *ac;

    ac=new QAction(this);
    ac->setSeparator(true);
    list.append(ac);

    ac=new QAction(QIcon(":/images/append.png"),"Add New Runtime Data",this);
    list.append(ac);
    connect(ac,SIGNAL(triggered()),this,SLOT(addNewData()));
    ac=new QAction(QIcon(":/images/remove.png"),"Remove Runtime Data",this);
    connect(ac,SIGNAL(triggered()),this,SLOT(removeSelect()));
    list.append(ac);

    ac=new QAction(this);
    ac->setSeparator(true);
    list.append(ac);

    ac=new QAction(QIcon(":/images/down.png"),"Move Down",this);
    connect(ac,SIGNAL(triggered()),this,SLOT(itemMoveDown()));
    list.append(ac);
    ac=new QAction(QIcon(":/images/up.png"),"Move Up",this);
    connect(ac,SIGNAL(triggered()),this,SLOT(itemMoveUp()));
    list.append(ac);

    m_toolbar->addButtonActions(list);
    m_colorList.append(QColor(200,200,255));

    m_colorList.append(QColor(192,220,230));
    m_colorList.append(QColor(180,255,200));
    m_colorList.append(QColor(255,192,192));
    m_colorList.append(QColor(255,230,220));

    connect(this,SIGNAL(itemChanged(QTreeWidgetItem*,int)),this,SLOT(itemChanged(QTreeWidgetItem*,int)));
    connect(this,SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),this,SLOT(itemDoubleClicked(QTreeWidgetItem*,int)));
    connect(this,SIGNAL(pressed(QModelIndex)),this,SLOT(itemPress(QModelIndex)));
}

void QRuntimeDatabaseView::itemPress(const QModelIndex &index)
{
    this->edit(index);
}

void QRuntimeDatabaseView::itemDoubleClicked(QTreeWidgetItem *item, int column)
{
    if(m_values.keys().indexOf(item)>=0)
    {
        if(column==3 || column==4)
        {
            if(item->text(column)=="True")
            {
                item->setText(column,"False");
            }
            else
            {
                item->setText(column,"True");
            }
        }
        else if(column==1 && item->text(2)=="Boolean")
        {
            if(item->text(column)=="True")
            {
                item->setText(column,"False");
            }
            else
            {
                item->setText(column,"True");
            }
            item->setData(1,Qt::UserRole,item->text(column)=="True");
        }
    }
}

void QRuntimeDatabaseView::addNewData()
{
    QAddRuntimeDataDialog dlg(this);
    dlg.setNameList(s_dataNames);
    dlg.setCategoryList(m_categorys.keys());
    if(this->selectedItems().size()!=0)
    {
        QTreeWidgetItem *item=this->selectedItems().first();
        if(m_categorys.values().indexOf(item)>=0)
        {
            dlg.setCurrentCateGory(item->text(0));
        }
        else if(m_values.keys().indexOf(item)>=0)
        {
            dlg.setCurrentCateGory(item->parent()->text(0));
        }
    }
    else
    {
        dlg.setCurrentCateGory("");
    }
    dlg.exec();
    QString name=dlg.getName();
    QString cate=dlg.getCategory();
    if(name!="" && cate!="")
    {
        QRuntimeData info;
        info.setName(name);
        info.setCategory(cate);
        QProject::project()->insertRuntimeData(-1,info);
        addRuntime(info);
    }

}

void QRuntimeDatabaseView::removeSelect()
{
    QTreeWidgetItem *item=this->selectedItems().first();
    if(item!=NULL)
    {
        if(m_values.keys().indexOf(item)>=0)
        {
            int k=QProject::project()->getRunningIndex(item->text(0));
            if(k>=0)
            {
                QProject::project()->removeRuntimeData(k);

            }
            m_values.remove(item);
            if(item->parent()->childCount()==1)
            {
                m_categorys.remove(item->parent()->text(0));
                delete item->parent();
            }
            else
            {
                delete item;
            }
        }
        else if(m_categorys.values().indexOf(item)>=0)
        {
            if(QMessageBox::warning(this,tr("Warning"),tr("Do you want to remove this category?")
                                    ,QMessageBox::Yes | QMessageBox::No)==QMessageBox::Yes)
            {
                int count=item->childCount();
                for(int i=0;i<count;i++)
                {
                    QTreeWidgetItem *child=item->child(i);
                    int k=QProject::project()->getRunningIndex(child->text(0));
                    if(k>=0)
                    {
                        QProject::project()->removeRuntimeData(k);
                    }
                    m_values.remove(child);
                }
                m_categorys.remove(item->text(0));
                delete item;
            }
        }
    }
}


QWidget *QRuntimeDatabaseView::toolBar()
{
    return m_toolbar;
}

void QRuntimeDatabaseView::addRuntime(const QRuntimeData &info)
{
    QRuntimeData in=info;
    QTreeWidgetItem *item=m_categorys.value(in.category(),NULL);
    if(item==NULL)
    {
        item=new QTreeWidgetItem(this);
        item->setText(0,in.category());
        item->setData(0,Qt::UserRole,m_categorys.values().size());
    }
    QTreeWidgetItem *data=new QTreeWidgetItem(item);
    data->setText(0,in.name());
    data->setText(1,in.valueText());
    data->setText(2,in.type());
    data->setText(3,in.holdOnExit()?"True":"False");
    data->setText(4,in.needSave()?"True":"False");
    data->setText(5,QString::number(in.saveTime()));
    data->setText(6,in.information());
    data->setFlags(data->flags() | Qt::ItemIsEditable);
    data->setData(1,Qt::UserRole,in.value());
    s_dataNames.append(in.name());
    m_categorys.insert(in.category(),item);
    m_values.insert(data,in.name());

    this->expandAll();
}

void QRuntimeDatabaseView::clear()
{
    m_categorys.clear();
    m_values.clear();
    QTreeWidget::clear();
}

void QRuntimeDatabaseView::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
{
    QStyleOptionViewItemV3 opt=options;
    QColor c;
    QString cate;
    if(index.child(0,0).isValid())
    {
        cate=index.model()->data(index.model()->index(index.row(),0,index.parent())).toString();
    }
    else
    {
        cate=index.model()->data(index.model()->index(index.parent().row(),0,index.parent().parent())).toString();
    }
    c=getColor(cate);
    opt.palette.setColor(QPalette::AlternateBase,c.lighter(112));
    painter->fillRect(opt.rect,c);
    opt.state &=~ QStyle::State_HasFocus;
    QTreeWidget::drawRow(painter,opt,index);

    opt.palette.setCurrentColorGroup(QPalette::Active);
    QColor color=static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor,&opt));
    painter->setPen(color);
    painter->drawLine(opt.rect.bottomLeft(),opt.rect.bottomRight());
}

QColor QRuntimeDatabaseView::getColor(const QString &category)const
{
    QTreeWidgetItem *item=m_categorys.value(category);
    int index=item->data(0,Qt::UserRole).toInt();
    int s=m_colorList.size();
    return m_colorList.at(index%s);
}

void QRuntimeDatabaseView::itemChanged(QTreeWidgetItem *item, int column)
{
    QString name=m_values.value(item);
    if(name=="")
    {
        return;
    }
    QProject *p=QProject::project();
    int index=p->getRunningIndex(name);
    if(index==-1)
    {
        return;
    }
    QRuntimeData r=p->getRuntimeData(index);
    QString data=item->text(column);
    if(column==0)
    {
        r.setName(data);
    }
    else if(column==1)
    {
        r.setValue(item->data(1,Qt::UserRole));
    }
    else if(column==2)
    {
        r.setType(data);
        item->setText(1,r.valueText());
    }
    else if(column==3)
    {
        r.setHoldOnExit(data=="True");
    }
    else if(column==4)
    {
        r.setNeedSave(data=="True");
    }
    else if(column==5)
    {
        r.setSaveTime(data.toInt());
    }
    else if(column==6)
    {
        r.setInformation(data);
    }
    else
    {
        return;
    }
    p->setRuntimeData(index,r);
}

void QRuntimeDatabaseView::itemMoveDown()
{
    QTreeWidgetItem *item=this->selectedItems().first();
    if(m_values.keys().indexOf(item)>=0)
    {
        QString name=m_values.value(item);
        int index=QProject::project()->getRunningIndex(name);
        QProject::project()->moveRuntimeDataDown(index);
        int in=item->parent()->indexOfChild(item);
        QTreeWidgetItem *p=item->parent();
        if(in<p->childCount()-1)
        {
            p->removeChild(item);
            p->insertChild(in+1,item);
            this->clearSelection();
            item->setSelected(true);
        }

    }
}

void QRuntimeDatabaseView::itemMoveUp()
{
    QTreeWidgetItem *item=this->selectedItems().first();
    if(m_values.keys().indexOf(item)>=0)
    {
        QString name=m_values.value(item);
        int index=QProject::project()->getRunningIndex(name);
        QProject::project()->moveRuntimeDataUp(index);
        int in=item->parent()->indexOfChild(item);
        QTreeWidgetItem *p=item->parent();
        if(in>0)
        {
            p->removeChild(item);
            p->insertChild(in-1,item);
            this->clearSelection();
            item->setSelected(true);
        }

    }
}
